﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information
// 
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to you under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
// 
// http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// 

using System;

using log4net;
using log4net.Core;
using log4net.Util;

namespace log4net.Filter
{
    /// <summary>This is a simple filter based on <see cref="Level"/> matching.</summary>
    /// <remarks>
    /// <para>
    /// The filter admits three options <see cref="LevelMin"/> and <see cref="LevelMax"/>
    /// that determine the range of priorities that are matched, and
    /// <see cref="AcceptOnMatch"/>. If there is a match between the range
    /// of priorities and the <see cref="Level"/> of the <see cref="LoggingEvent"/>, then the 
    /// <see cref="Decide"/> method returns <see cref="FilterDecision.Accept"/> in case the <see cref="AcceptOnMatch"/> 
    /// option value is set to <c>true</c>, if it is <c>false</c>
    /// then <see cref="FilterDecision.Deny"/> is returned. If there is no match, <see cref="FilterDecision.Deny"/> is returned.
    /// </para>
    /// </remarks>
    /// <author>Nicko Cadell</author>
    /// <author>Gert Driesen</author>
    public class LevelRangeFilter : FilterSkeleton
    {
        /// <summary>Flag to indicate the behavior when matching a <see cref="Level"/></summary>
        private bool m_acceptOnMatch = true;

        /// <summary>the minimum <see cref="Level"/> value to match</summary>
        private Level m_levelMin;

        /// <summary>the maximum <see cref="Level"/> value to match</summary>
        private Level m_levelMax;

        /// <summary>Default constructor</summary>
        public LevelRangeFilter()
        {
        }

        /// <summary><see cref="FilterDecision.Accept"/> when matching <see cref="LevelMin"/> and <see cref="LevelMax"/></summary>
        /// <remarks>
        /// <para>
        /// The <see cref="AcceptOnMatch"/> property is a flag that determines
        /// the behavior when a matching <see cref="Level"/> is found. If the
        /// flag is set to true then the filter will <see cref="FilterDecision.Accept"/> the 
        /// logging event, otherwise it will <see cref="FilterDecision.Neutral"/> the event.
        /// </para>
        /// <para>
        /// The default is <c>true</c> i.e. to <see cref="FilterDecision.Accept"/> the event.
        /// </para>
        /// </remarks>
        public bool AcceptOnMatch
        {
            get { return this.m_acceptOnMatch; }
            set { this.m_acceptOnMatch = value; }
        }

        /// <summary>Set the minimum matched <see cref="Level"/></summary>
        /// <remarks>
        /// <para>
        /// The minimum level that this filter will attempt to match against the 
        /// <see cref="LoggingEvent"/> level. If a match is found then
        /// the result depends on the value of <see cref="AcceptOnMatch"/>.
        /// </para>
        /// </remarks>
        public Level LevelMin
        {
            get { return this.m_levelMin; }
            set { this.m_levelMin = value; }
        }

        /// <summary>Sets the maximum matched <see cref="Level"/></summary>
        /// <remarks>
        /// <para>
        /// The maximum level that this filter will attempt to match against the 
        /// <see cref="LoggingEvent"/> level. If a match is found then
        /// the result depends on the value of <see cref="AcceptOnMatch"/>.
        /// </para>
        /// </remarks>
        public Level LevelMax
        {
            get { return this.m_levelMax; }
            set { this.m_levelMax = value; }
        }

        /// <summary>Check if the event should be logged.</summary>
        /// <param name="loggingEvent">the logging event to check</param>
        /// <returns>see remarks</returns>
        /// <remarks>
        /// <para>
        /// If the <see cref="Level"/> of the logging event is outside the range
        /// matched by this filter then <see cref="FilterDecision.Deny"/>
        /// is returned. If the <see cref="Level"/> is matched then the value of
        /// <see cref="AcceptOnMatch"/> is checked. If it is true then
        /// <see cref="FilterDecision.Accept"/> is returned, otherwise
        /// <see cref="FilterDecision.Neutral"/> is returned.
        /// </para>
        /// </remarks>
        public override FilterDecision Decide(LoggingEvent loggingEvent) 
        {
            if (loggingEvent == null)
            {
                throw new ArgumentNullException("loggingEvent");
            }

            if (this.m_levelMin != null) 
            {
                if (loggingEvent.Level < this.m_levelMin) 
                {
                    // level of event is less than minimum
                    return FilterDecision.Deny;
                }
            }

            if (this.m_levelMax != null) 
            {
                if (loggingEvent.Level > this.m_levelMax) 
                {
                    // level of event is greater than maximum
                    return FilterDecision.Deny;
                }
            }

            if (this.m_acceptOnMatch) 
            {
                // this filter set up to bypass later filters and always return
                // accept if level in range
                return FilterDecision.Accept;
            }
            else 
            {
                // event is ok for this filter; allow later filters to have a look..
                return FilterDecision.Neutral;
            }
        }
    }
}
